package com.tigger.common.utils;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Created by root on 2017/9/13.
 */
public class ReflectUtils {

    final static Logger LOGGER = LoggerFactory.getLogger(ReflectUtils.class);

    /**
     * 通过构造函数实例化对象
     *
     * @param className      类的全路径名称
     * @param parameterTypes 参数类型
     * @param initargs       参数值
     * @return
     */
    public static Object constructorNewInstance(String className, Class[] parameterTypes, Object[] initargs) {
        try {
            Constructor<?> constructor = (Constructor<?>) Class.forName(className)
                    .getDeclaredConstructor(parameterTypes);
            constructor.setAccessible(true);
            return constructor.newInstance(initargs);
        } catch (Exception ex) {
            throw new RuntimeException();
        }

    }

    /**
     * 获取字段值
     *
     * @param propertyName 属性名
     * @param object       实例对象
     * @return
     */
    public static Object getProperty(String propertyName, Object object) {
        try {
            PropertyDescriptor pd = new PropertyDescriptor(propertyName, object.getClass());
            Method method = pd.getReadMethod();
            return method.invoke(object);
        } catch (Exception ex) {
            throw new RuntimeException();
        }
    }

    /**
     * 通过BeanUtils工具包获取反射获取字段值，值是以字符串形式存在的
     *
     * @param propertyName
     * @param object
     * @return
     */
    public static Object getBeanInfoProperty(String propertyName, Object object) {
        try {
            return BeanUtils.getProperty(object, propertyName);
        } catch (Exception ex) {
            throw new RuntimeException();
        }
    }

    /**
     * 过BeanUtils工具包获取反射获取字段值，值是以字符串形式存在的
     *
     * @param object
     * @param propertyName
     * @param value
     */
    public static void setBeanInfoProperty(Object object, String propertyName, String value) {
        try {
            BeanUtils.setProperty(object, propertyName, value);
        } catch (Exception ex) {
            throw new RuntimeException();
        }
    }

    /**
     * 通过BeanUtils工具包获取反射获取字段值，值是以对象属性的实际类型
     *
     * @param propertyName
     * @param object
     * @return
     */
    public static Object getPropertyUtilByName(String propertyName, Object object) {
        try {
            return PropertyUtils.getProperty(object, propertyName);
        } catch (Exception ex) {
            throw new RuntimeException();
        }
    }

    /**
     * 通过BeanUtils工具包获取反射获取字段值，值是以对象属性的实际类型
     *
     * @param object
     * @param propertyName
     * @param value
     */
    public static void setPropertyUtilByName(Object object, String propertyName, Object value) {
        try {
            PropertyUtils.setProperty(object, propertyName, value);
        } catch (Exception ex) {
            throw new RuntimeException();
        }
    }

    /**
     * 设置字段值
     *
     * @param object
     * @param propertyName
     * @param value
     * @throws IntrospectionException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    public static void setProperties(Object object, String propertyName, Object value) throws IntrospectionException,
            IllegalAccessException, InvocationTargetException {
        PropertyDescriptor pd = new PropertyDescriptor(propertyName, object.getClass());
        Method methodSet = pd.getWriteMethod();
        methodSet.invoke(object, value);
    }

    /**
     * 设置字段值
     *
     * @param className      类的全路径名称
     * @param methodName     调用方法名
     * @param parameterTypes 参数类型
     * @param values         参数值
     * @param object         实例对象
     * @return
     */
    public static Object methodInvoke(String className, String methodName, Class[] parameterTypes, Object[] values, Object object) {
        try {
            Method method = Class.forName(className).getDeclaredMethod(methodName, parameterTypes);
            method.setAccessible(true);
            return method.invoke(object, values);
        } catch (Exception ex) {
            throw new RuntimeException();
        }
    }

    /**
     * 对象比较
     *
     * @param fileds     要进行比较Bean对象的属性值集合
     * @param sourceBean
     * @param targetBean
     * @param <T>
     * @return
     */
    public static <T> String compareBeanValue(Map<String, String> fileds, T sourceBean, T targetBean) {
        StringBuilder compares = new StringBuilder();
        String propertyName = null;
        Object oldPropertyValue = null;
        Object newPropertyValue = null;

        for (Map.Entry<String, String> entity : fileds.entrySet()) {
            propertyName = entity.getKey().toLowerCase();
            oldPropertyValue = getProperty(propertyName, sourceBean);
            newPropertyValue = getProperty(propertyName, targetBean);

            if (null == oldPropertyValue && null == newPropertyValue) {
                continue;
            }
            if ("".equals(oldPropertyValue) && "".equals(newPropertyValue)) {
                continue;
            }
            if (null == oldPropertyValue) {
                oldPropertyValue = "";
            }
            if (null == newPropertyValue) {
                newPropertyValue = "";
            }

            if (oldPropertyValue.equals(newPropertyValue)) {
                continue;
            }
            compares.append("字段注释: ").append(entity.getValue()).append("]").append("原属性值\"");
            if (StringUtils.isEmpty(oldPropertyValue + "")) {
                oldPropertyValue = " ";
            }
            compares.append(oldPropertyValue).append("\"现属性值\"");
            if (StringUtils.isEmpty(newPropertyValue + "")) {
                newPropertyValue = " ";
            }
            compares.append(newPropertyValue).append("\";");
        }
        return compares.toString();
    }

    /**
     * 获取字段
     *
     * @param targetClass
     * @param fieldName
     * @return
     */
    public static Field getTargetField(Class<?> targetClass, String fieldName) {
        Field field = null;
        try {
            if (targetClass == null) {
                return field;
            }

            if (Object.class.equals(targetClass)) {
                return field;
            }

            field = FieldUtils.getDeclaredField(targetClass, fieldName, true);
            if (field == null) {
                field = getTargetField(targetClass.getSuperclass(), fieldName);
            }
        } catch (Exception ex) {
            LOGGER.error("Get field error: s%", ex.getMessage());
        }
        return field;
    }

    public static Field getTargetFieldIgnoreCase(Class<?> targetClass, String fieldName) {
        Field field = null;
        try {
            if (targetClass == null) {
                return field;
            }

            if (Object.class.equals(targetClass)) {
                return field;
            }

            Field[] fields = FieldUtils.getAllFields(targetClass);
            for (Field f : fields) {
                if (StringUtils.equalsIgnoreCase(f.getName(), fieldName))
                    field = f;
            }

            if (field == null) {
                field = getTargetField(targetClass.getSuperclass(), fieldName);
            }
        } catch (Exception ex) {
            LOGGER.error("Get field error: s%", ex.getMessage());
        }
        return field;
    }

    /**
     * 获取字段值
     *
     * @param obj
     * @param fieldName
     * @return
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        if (obj == null) {
            return null;
        }
        Field targetField = getTargetField(obj.getClass(), fieldName);

        try {
            return FieldUtils.readField(targetField, obj, true);
        } catch (IllegalAccessException ex) {
            LOGGER.error("Get field value error: s%", ex.getMessage());
        }
        return null;
    }

    /**
     * 设置字段值，忽略字段名称大小写形式
     *
     * @param bean
     * @param fieldName
     * @param value
     */
    public static void setFieldValueIgnoreCase(Object bean, String fieldName, String value) {
        if (null == bean) return;
        Field field = getTargetField(bean.getClass(), fieldName);
        try {
            FieldUtils.writeField(field, bean, value);
        } catch (IllegalAccessException ex) {
            LOGGER.error("Set field value error: s%", ex.getMessage());
        }
    }

    /**
     * 设置字段值
     *
     * @param bean
     * @param fieldName
     * @param value
     */
    public static void setFieldValue(Object bean, String fieldName, String value) {
        if (null == bean) return;
        Field field = getTargetField(bean.getClass(), fieldName);
        try {
            FieldUtils.writeField(field, bean, value);
        } catch (IllegalAccessException ex) {
            LOGGER.error("Set field value error: s%", ex.getMessage());
        }
    }
}
